package ru.amse.tsyganov.jumleditor.view;

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;

import ru.amse.tsyganov.jumleditor.model.State;
import ru.amse.tsyganov.jumleditor.model.Transition;

public final class TransitionDraughtsman {

	public static void drawTransition(GC gc, 
			StateView<? extends State> target, 
			StateView<? extends State> source) {
		
		if (target == null || source == null) {
			throw new IllegalArgumentException();
		}
		
		if (source.getRectangle().intersects(target.getRectangle())) {
			return;
		}
		
		Line transitionLine = getLine(
				source.getRectangle(), 
				target.getRectangle());
			
		drawArrowedLine(gc, transitionLine, false);
	}
	
	public static void drawArrowedLine(GC gc, Line line, boolean arrowAtStart) {
		final int LIMIT_LINE_LENGTH = 4; 
		if (line.getLength() < LIMIT_LINE_LENGTH) {
			return;
		}
		int sourceX = arrowAtStart ? line.x1 : line.x2;
		int sourceY = arrowAtStart ? line.y1 : line.y2;
		
		int targetX = arrowAtStart ? line.x2 : line.x1;
		int targetY = arrowAtStart ? line.y2 : line.y1;


		double a = Math.atan2(targetY - sourceY, 
				sourceX - targetX);
		
		double a1 = a + Math.PI / 15;
		double a2 = a - Math.PI / 15;
		double len = 20;
		
		gc.setAntialias(SWT.ON);
		gc.drawLine(line.x1, line.y1,
				line.x2, line.y2);

		//gc.setBackground(new Color(null, new RGB(10, 10, 10)));
		gc.fillPolygon(new int[] {
				targetX, targetY, 
				(int) (targetX + len * Math.cos(a1)), 
				(int) (targetY - len * Math.sin(a1)),
				(int) (targetX + len * Math.cos(a2)), 
				(int) (targetY - len * Math.sin(a2))});
	}
	
	public static void drawTransition(GC gc, 
			TransitionView<? extends Transition> t) {
		
		if (gc == null || t == null) {
			throw new IllegalArgumentException();
		}
		
		StateView<? extends State> target = 
			ModelViewMap.map.getStatesMap().get(
					t.getModel().getTarget().getContainer());
		
		StateView<? extends State> source = 
			ModelViewMap.map.getStatesMap().get(
					t.getModel().getSource().getContainer());
		
		drawTransition(gc, target, source);
	}
	
	public static Line getLine(Rectangle rect1, Rectangle rect2) {
		Line line = new Line();
		
		Point centerPointOfFirstRect = 
			new Point(rect1.x + rect1.width / 2,
					rect1.y + rect1.height / 2);
		
		Point centerPointOfSecondRect = 
			new Point(rect2.x + rect2.width / 2,
					rect2.y + rect2.height / 2);
		
		Point connectionPoint1 = getConnectionPoint(centerPointOfFirstRect, rect2);
		line.x1 = connectionPoint1.x;
		line.y1 = connectionPoint1.y;
		
		Point connectionPoint2 = getConnectionPoint(centerPointOfSecondRect, rect1);
		line.x2 = connectionPoint2.x;
		line.y2 = connectionPoint2.y;
		
		return line;
	}
	
	public static Line getLine(Point p, Rectangle rect) {
		Line line = new Line();
		
		Point connectionPoint = getConnectionPoint(p, rect);
		line.x1 = p.x;
		line.y1 = p.y;
		line.x2 = connectionPoint.x;
		line.y2 = connectionPoint.y;
		
		return line;
	}
	
	public static Point getConnectionPoint(
			Point sourcePoint, 
			Rectangle targetRect) { 
		
		double targetCX = targetRect.x + targetRect.width / 2.;
		double targetCY = targetRect.y + targetRect.height / 2.;
		
		int tx;
		int ty;
		
		if (((double) Math.abs(sourcePoint.x - targetCX) / 
				(double) Math.abs(sourcePoint.y - targetCY)) > 
					((double) targetRect.width / (double) targetRect.height)) {
			
			double dy = ((double) targetRect.width / 2) * 
				((double) (targetCY - sourcePoint.y) / 
						(double) (sourcePoint.x - targetCX));
			
			if (sourcePoint.x > targetCX) {
				tx = (int) (targetRect.x + targetRect.width);
				ty = (int) (targetCY - dy);
			} else {
				tx = (int) targetRect.x;
				ty = (int) (targetCY + dy);
			}
		} else {
			
			double dx = ((double) targetRect.height / 2) * 
				((double)(sourcePoint.x - targetCX) / 
						(double) (targetCY - sourcePoint.y));
			
			if (sourcePoint.y > targetCY) {
				tx = (int) (targetCX - dx);
				ty = (int) (targetRect.y + targetRect.height);
			} else {
				tx = (int) (targetCX + dx);
				ty = (int) (targetRect.y);
			}
		}
		return new Point(tx, ty);
	}
}
